﻿using Moq;
using System;
using System.Linq;
using System.Web.Mvc;
using Stimulus360.Services;
using Stimulus360.Models.Data;
using Stimulus360.Controllers;
using System.Collections.Generic;
using Stimulus360.Controllers.Ideas;
using Stimulus360.Tests.Repositories;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Stimulus360.Tests.Controllers
{
    /// <summary>
    ///    This class contains test methods to test the Ideas Controller
    /// </summary>
    [TestClass]
    public class IdeasControllerTest
    {
        private TestContext _testContextInstance;

        /// <summary>
        ///     Gets or sets the test context which provides
        ///     information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return _testContextInstance;
            }
            set
            {
                _testContextInstance = value;
            }
        }

        /// <summary>
        ///     Method to create  dependency injected IdeasController
        /// </summary>
        /// <returns>
        ///     Idea Controller with a IdeaService injected with fake repository
        /// </returns>
        private static IdeasController CreateIdeasController()
        {
            List<Idea> testIdeas = FakeData.CreateIdeas();
            var repository = new FakeDataRepository(testIdeas, null);
            IdeaService ideaService = new IdeaService(repository);
            InvestmentCategoryService investmentService = new InvestmentCategoryService(repository);
            IdeasController controller = new IdeasController(ideaService, investmentService);
            return controller;
        }

        /// <summary>
        ///     Method to create a dependency injected IdeasController with the HTTPContext user
        ///     and authorization set.
        /// </summary>
        /// <param name="userName">
        ///     The username to set in the HTTP context
        /// </param>
        /// <returns>
        ///     Dependency injected IdeasController with the HTTPContext user
        ///     and authorization set.
        /// </returns>
        private static IdeasController CreateIdeasControllerAs(string userName)
        {

            var mock = new Mock<ControllerContext>();
            mock.SetupGet(p => p.HttpContext.User.Identity.Name).Returns(userName);
            mock.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true);

            var controller = CreateIdeasController();
            controller.ControllerContext = mock.Object;

            return controller;
        }
        /// <summary>
        ///     Method to test adding a valid idea
        /// </summary>
        [TestMethod]
        public void NewIdea_TestPass()
        {

            List<Idea> testIdeas = FakeData.CreateIdeas();
            var repository = new FakeDataRepository(testIdeas, null);
            IdeaService ideaService = new IdeaService(repository);
            InvestmentCategoryService investmentService = new InvestmentCategoryService(repository);
            IdeasController controller = new IdeasController(ideaService, investmentService);

            Mock<ControllerContext> mockControllerContext = new Mock<ControllerContext>();
            mockControllerContext.Setup(c => c.HttpContext.Request.IsAuthenticated).Returns(true);
            mockControllerContext.Setup(c => c.HttpContext.User.Identity.Name).Returns("Admin");
            controller.ControllerContext = mockControllerContext.Object;
            Idea idea = FakeData.CreateIdea();
            var result = controller.New(idea);
            Assert.AreNotEqual(20, repository.GetAllIdeas().Count());
            Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));

        }
        /// <summary>
        ///     Method to check adding an idea with invalid Title
        /// </summary>
        [TestMethod]
        public void NewIdea_TestFail_NoTitle()
        {

            IdeasController controller = CreateIdeasControllerAs("Admin");
            Idea idea = FakeData.CreateIdea();
            idea.Title = "";
            var result = (ViewResult)controller.New(idea);
            Assert.IsInstanceOfType(result.ViewData.Model, typeof(NewIdeaViewModel));

            Assert.IsTrue(result.ViewData.ModelState.IsValid == false);
            Assert.AreEqual(1, result.ViewData.ModelState.Sum(p => p.Value.Errors.Count), "Expected Errors");
            ModelState model = result.ViewData.ModelState["Title"];
            Assert.IsNull(model.Value);
            Assert.IsTrue(model.Errors.Count == 1);

        }
        
        /// <summary>
        ///     Method to test adding an idea with invalid description
        /// </summary>
        [TestMethod]
        public void NewIdea_TestFail_NoDescription()
        {

            IdeasController controller = CreateIdeasControllerAs("Admin");
            Idea idea = FakeData.CreateIdea();
            idea.Description  = "";
            var result = (ViewResult)controller.New(idea);
            Assert.IsInstanceOfType(result.ViewData.Model, typeof(NewIdeaViewModel));

            Assert.IsTrue(result.ViewData.ModelState.IsValid == false);
            Assert.AreEqual(1, result.ViewData.ModelState.Sum(p => p.Value.Errors.Count), "Expected Errors");
            ModelState model = result.ViewData.ModelState["Description"];
            Assert.IsNull(model.Value);
            Assert.IsTrue(model.Errors.Count == 1);

        }

        /// <summary>
        ///     Method to check adding an idea with invalid Category
        /// </summary>
        [TestMethod]
        public void NewIdea_TestFail_NoCategory()
        {

            IdeasController controller = CreateIdeasControllerAs("Admin");
            Idea idea = FakeData.CreateInvalidCategoryIdea() ;
            var result = (ViewResult)controller.New(idea);
            Assert.IsInstanceOfType(result.ViewData.Model, typeof(NewIdeaViewModel));

            Assert.IsTrue(result.ViewData.ModelState.IsValid == false);
            Assert.AreEqual(1, result.ViewData.ModelState.Sum(p => p.Value.Errors.Count), "Expected Errors");
            ModelState model = result.ViewData.ModelState["CategoryId"];
            Assert.IsNull(model.Value);
            Assert.IsTrue(model.Errors.Count == 1);

        }

        /// <summary>
        ///     Test method to search Ideas for Accepted status
        /// </summary>
        [TestMethod]
        public void SearchByStatus()
        {
            //Search by status
            IdeasController ideasController = CreateIdeasController() ;
            const string criteria = "Status";
            const string query = "Accepted";
            Nullable<int> page = new Nullable<int>();
            var result = (ViewResult)ideasController.Search(criteria, query, page);
            Assert.IsInstanceOfType(result.ViewData.Model, typeof(IdeasSearchViewModel));
            IdeasSearchViewModel model = (IdeasSearchViewModel)result.ViewData.Model;
            bool statusAccepted = true;
            foreach (Idea idea in model.Ideas)
            {
                if (idea.Status != (byte)IdeaStatus.Accepted)
                {
                    statusAccepted = false;
                }
            }
            Assert.IsTrue(statusAccepted);

        }

        /// <summary>
        ///    Test method to search Ideas by ranking.
        /// </summary>
        [TestMethod]
        public void SearchByRanking()
        {
            //Search by Ranking
            IdeasController ideasController = CreateIdeasController() ;
            const string criteria = "Top";
            const string query = "6";
            Nullable<int> page = new Nullable<int>();
            var result = (ViewResult)ideasController.Search(criteria, query, page);
            Assert.IsInstanceOfType(result.ViewData.Model, typeof(IdeasSearchViewModel));
            IdeasSearchViewModel model = (IdeasSearchViewModel)result.ViewData.Model;
            Assert.AreEqual(model.Ideas.TotalCount, 6);
        }

        /// <summary>
        ///     Test method to search Ideas for given search string
        /// </summary>
        [TestMethod]
        public void SearchByText()
        {
            //Search by Text
            IdeasController ideasController = CreateIdeasController();
            const string criteria = "SearchText";
            const string query = "Child";
            Nullable<int> page = new Nullable<int>();
            var result = (ViewResult)ideasController.Search(criteria, query, page);
            Assert.IsInstanceOfType(result.ViewData.Model, typeof(IdeasSearchViewModel));
            IdeasSearchViewModel model = (IdeasSearchViewModel)result.ViewData.Model;
            bool searchTextValid = true;
            foreach (Idea idea in model.Ideas)
            {
                if (!((idea.Title.ToLower().Contains(query.ToLower())) || (idea.InvestmentCategory.Category.ToLower().Contains(query.ToLower()))))
                {
                    searchTextValid = false;
                }
            }
            Assert.IsTrue(searchTextValid);
        }

    }
}